package com.example.upload.config;

import cn.hutool.core.util.NumberUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.math.BigDecimal;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @description: TODO
 * @author: song
 * @date: 2023-08-21 14:43
 **/
@Configuration
public class EventPoolConfig {

    /**
     * 最大线程池数量
     */
    private static final Integer threadNumMax=16;
    /**
     * 线程池被销毁时，等待线程任务执行结束时间（单位：秒）
     */
    private static final Integer awaitTime=30;

    @Bean(value = "asyncExecutorEvent")
    public ThreadPoolTaskExecutor  asyncExecutorEvent(){

        Integer threadNum=Runtime.getRuntime().availableProcessors()>4?Runtime.getRuntime().availableProcessors()-1:4;
        if(threadNum.intValue()>threadNumMax.intValue()){
            threadNum=threadNumMax;
        }
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数
        executor.setCorePoolSize(threadNum);
        // 线程池维护线程的最大数量，只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setMaxPoolSize(new BigDecimal(threadNum.toString()).multiply(new BigDecimal("2")).stripTrailingZeros().intValue());
        // 缓存队列
        executor.setQueueCapacity(NumberUtil.mul(new BigDecimal(Short.MAX_VALUE),new BigDecimal("2")).intValue());
        // 空闲时间，当超过了核心线程数之外的线程在空闲时间到达之后会被销毁
        executor.setKeepAliveSeconds(180);
        // 异步方法内部线程名称
        executor.setThreadNamePrefix("async-executor-event");

        /**
         * 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize，如果还有任务到来就会采取任务拒绝策略
         * 通常有以下四种策略：
         * ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
         * ThreadPoolExecutor.DiscardPolicy：也是丢弃任务，但是不抛出异常。
         * ThreadPoolExecutor.DiscardOldestPolicy：丢弃队列最前面的任务，然后重新尝试执行任务（重复此过程）
         * ThreadPoolExecutor.CallerRunsPolicy：重试添加当前的任务，自动重复调用 execute() 方法，直到成功
         */
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(awaitTime);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        executor.initialize();

        return executor;
    }

}
